package service.impl;

import cn.tedu.csmall.commons.ex.ServiceException;
import cn.tedu.csmall.commons.pojo.vo.PageData;
import cn.tedu.csmall.commons.util.PageInfoToPageDataConverter;
import cn.tedu.csmall.commons.web.ServiceCode;
import cn.tedu.csmall.passport.mapper.AdminMapper;
import cn.tedu.csmall.passport.mapper.AdminRoleMapper;
import cn.tedu.csmall.passport.pojo.entity.Admin;
import cn.tedu.csmall.passport.pojo.entity.AdminRole;
import cn.tedu.csmall.passport.pojo.param.AdminAddNewParam;
import cn.tedu.csmall.passport.pojo.param.AdminLoginInfoParam;
import cn.tedu.csmall.passport.pojo.param.AdminUpdateParam;
import cn.tedu.csmall.passport.pojo.vo.AdminListItemVO;
import cn.tedu.csmall.passport.pojo.vo.AdminStandardVO;
import cn.tedu.csmall.passport.security.AdminDetails;
import cn.tedu.csmall.passport.service.IAdminService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

@Service
@Slf4j
public class AdminServiceImpl implements IAdminService {

    @Value("${csmall.jwt.secret-key}")
    private String secretKey;
    @Value("${csmall.jwt.duration-in-minute}")
    private Long durationInMinute;

    @Autowired
    private AdminMapper mapper;
    @Autowired
    private AdminRoleMapper adminRoleMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public String login(AdminLoginInfoParam adminLoginInfoParam) {
        log.info("【开始登录】");
        log.debug("传入参数,adminLoginInfoParam={}", adminLoginInfoParam);
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                adminLoginInfoParam.getUsername(), adminLoginInfoParam.getPassword());
        Authentication authenticationResult = authenticationManager.authenticate(authentication);
        log.info("【验证登录完成】");

        AdminDetails adminDetails = (AdminDetails) authenticationResult.getPrincipal();
        Collection<GrantedAuthority> authorities = adminDetails.getAuthorities();

        Map<String, Object> claims = new HashMap<>();
        claims.put("id", adminDetails.getId());
        claims.put("username", adminDetails.getUsername());
        claims.put("authoritiesJsonString", JSON.toJSONString(authorities));


        String jwt = Jwts.builder()
                // Header
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                // Payload
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + durationInMinute * 60 * 1000))
                // Verify Signature
                .signWith(SignatureAlgorithm.HS256, secretKey)
                // 生成
                .compact();
        return jwt;
    }

    @Override
    public void addNew(AdminAddNewParam adminAddNewParam) {
        log.info("开始:【新增用户】");
        log.debug("传入参数,adminAddNewParam={}", adminAddNewParam);
        //检查用户名称是否被占用，如果被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("username",adminAddNewParam.getUsername());
        int countByUsername = mapper.selectCount(queryWrapper2);
        log.info("规则：根据用户名称检索数据库结果,countByUsername={}", countByUsername);
        if(countByUsername > 0){
            log.info("结果2:不满足用户新增规则【用户名不可重复】，抛出自定异常...");
            throw new ServiceException(ServiceCode.ERR_CONFLICT, "添加用户失败，用户名已存在！");
        }

        //检查手机号称是否被占用，如果被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper3 = new QueryWrapper<>();
        queryWrapper3.eq("phone",adminAddNewParam.getPhone());
        int countByPhone = mapper.selectCount(queryWrapper3);
        log.info("规则：根据手机号称检索数据库结果,countByPhone={}", countByPhone);
        if(countByPhone > 0){
            log.info("结果3:不满足用户新增规则【手机号不可重复】，抛出自定异常...");
            throw new ServiceException(ServiceCode.ERR_CONFLICT, "添加用户失败，手机号已存在！");
        }

        //检查邮箱称是否被占用，如果被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper4 = new QueryWrapper<>();
        queryWrapper4.eq("email",adminAddNewParam.getPhone());
        int countByEmail = mapper.selectCount(queryWrapper4);
        log.info("规则：根据邮箱检索数据库结果,countByEmail={}", countByEmail);
        if(countByEmail > 0){
            log.info("结果4:不满足用户新增规则【邮箱不可重复】，抛出自定异常...");
            throw new ServiceException(ServiceCode.ERR_CONFLICT, "添加用户失败，邮箱已存在！");
        }

        log.info("满足用户新增规则，开始新增...");
        log.info("新增用户数据...");
        //名称未占用，添加用户到数据库
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminAddNewParam,admin);
        admin.setGmtCreate(LocalDateTime.now()).setGmtModified(LocalDateTime.now())
                .setLoginCount(0).setLastLoginIp(null).setGmtLastLogin(null);
        String pwd = admin.getPassword();
        String encodePwd = passwordEncoder.encode(pwd);
        admin.setPassword(encodePwd);
        
        log.debug("新增用户数据,admin={}", admin);
        int rows = mapper.insert(admin);
        if(rows != 1){
            String message = "添加管理员失败，服务器忙，请稍后再试！";
            log.info("结果1:数据返回行数不满足预估行数，预估={}, 实际={}, 抛出自定异常...", 1, rows);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
        log.info("结果1:新增用户结果,rows={}", rows);

        log.info("新增用户角色关系数据...");
        Long[] roleIds = adminAddNewParam.getRoleIds();
        AdminRole[] adminRoles = new AdminRole[roleIds.length];
        LocalDateTime now = LocalDateTime.now();
        for (int i = 0; i < adminRoles.length; i++) {
            AdminRole adminRole = new AdminRole();
            adminRole.setAdminId(admin.getId()).setRoleId(roleIds[i])
                    .setGmtCreate(now).setGmtModified(now);
            adminRoles[i] = adminRole;
        }
        log.debug("新增用户角色关系数据,adminRoles={}", adminRoles);
        rows = adminRoleMapper.insertBatch(adminRoles);
        if(rows != roleIds.length){
            String message = "添加管理员失败，服务器忙，请稍后再试！";
            log.info("结果1:数据返回行数不满足预估行数，预估={}, 实际={}, 抛出自定异常...", roleIds.length, rows);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
        log.info("结果1:新增用户角色关系结果,rows={}", rows);

    }

    @Override
    public void deleteById(Long id) {
        log.info("开始:【删除用户】");
        log.debug("传入参数,id={}", id);
        //检查用户ID检查用户是否存在数据，如果不存在，则抛出异常
        QueryWrapper<Admin> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("id", id);
        int countById = mapper.selectCount(queryWrapper2);
        log.info("规则：根据用户id检索数据库结果,countById={}", countById);
        if(countById == 0){
            log.info("结果2:根据用户id检索数据量为0，抛出异常...");
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, "删除用户失败，用户数据不存在！");
        }

        log.info("删除用户数据...");
        int rows = mapper.deleteById(id);
        if(rows != 1){
            String message = "删除用户失败，服务器忙，请稍后再试！";
            log.info("结果1:数据返回行数不满足预估行数，预估={}, 实际={}, 抛出自定异常...", 1, rows);
            throw new ServiceException(ServiceCode.ERR_DELETE, message);
        }
        log.info("结果1:删除用户结果,rows={}", rows);

        log.info("删除用户角色关系数据...");
        int rows2 = adminRoleMapper.deleteByAdminId(id);
        log.info("结果1:删除用户角色关系结果,rows2={}", rows2);
    }



    @Override
    public void updateInfoById(Long id, AdminUpdateParam adminUpdateParam) {
        log.info("开始:【更新用户信息】");
        log.debug("传入参数,id={}", id);
        //检查用户ID检查用户是否存在数据，如果不存在，则抛出异常
        QueryWrapper<Admin> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("id", id);
        int countById = mapper.selectCount(queryWrapper2);
        log.info("规则：根据用户id检索数据库结果,countById={}", countById);
        if(countById == 0){
            log.info("结果2:根据用户id检索数据量为0，抛出异常...");
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, "更新用户失败，用户数据不存在！");
        }

        //检查手机号称是否被其他账号占用，如果被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper3 = new QueryWrapper<>();
        queryWrapper3.eq("phone",adminUpdateParam.getPhone())
                .ne("id", id);
        int countByPhone = mapper.selectCount(queryWrapper3);
        log.info("规则：根据手机号称检索数据库结果,countByPhone={}", countByPhone);
        if(countByPhone > 0){
            log.info("结果3:不满足用户更新规则【手机号不可重复】，抛出自定异常...");
            throw new ServiceException(ServiceCode.ERR_CONFLICT, "更新用户失败，手机号已存在！");
        }

        //检查邮箱称是否被占用，如果被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper4 = new QueryWrapper<>();
        queryWrapper4.eq("email",adminUpdateParam.getEmail())
                .ne("id", id);;
        int countByEmail = mapper.selectCount(queryWrapper4);
        log.info("规则：根据邮箱检索数据库结果,countByEmail={}", countByEmail);
        if(countByEmail > 0){
            log.info("结果4:不满足用户更新规则【邮箱不可重复】，抛出自定异常...");
            throw new ServiceException(ServiceCode.ERR_CONFLICT, "更新用户失败，邮箱已存在！");
        }

        Admin admin = new Admin();
        BeanUtils.copyProperties(adminUpdateParam, admin);
        admin.setId(id);
        int rows = mapper.updateById(admin);
        if(rows != 1){
            String message = "编辑用户失败，服务器忙，请稍后再试！";
            log.info("结果1:数据返回行数不满足预估行数，预估={}, 实际={}, 抛出自定异常...", 1, rows);
            throw new ServiceException(ServiceCode.ERR_UPDATE, message);
        }
        log.info("结果1:根据id编辑用户结果,rows={}", rows);
    }

    @Override
    public void updatePwdById(Long id, String password) {
        log.info("开始:【更新密码】");
        log.debug("传入参数,id={}", id);

        //检查用户ID检查用户是否存在数据，如果不存在，则抛出异常
        QueryWrapper<Admin> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("id", id);
        int countById = mapper.selectCount(queryWrapper2);
        log.info("规则：根据用户id检索数据库结果,countById={}", countById);
        if(countById == 0){
            log.info("结果2:根据用户id检索数据量为0，抛出异常...");
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, "删除用户失败，用户数据不存在！");
        }

        Admin admin = new Admin();
        admin.setId(id);
        String encodePwd = passwordEncoder.encode(password);
        admin.setPassword(encodePwd);
        log.debug("更新密码数据,admin={}", admin);
        int rows = mapper.updateById(admin);
        if(rows != 1){
            String message = "更新密码失败，服务器忙，请稍后再试！";
            log.info("结果1:数据返回行数不满足预估行数，预估={}, 实际={}, 抛出自定异常...", 1, rows);
            throw new ServiceException(ServiceCode.ERR_UPDATE, message);
        }
        log.info("结果1:根据id编辑密码结果,rows={}", rows);

    }

    @Override
    public AdminStandardVO getStandardById(Long id) {
        log.info("开始:【根据ID查询用户】");
        log.debug("传入参数,id={}", id);
        AdminStandardVO standardById = mapper.getStandardById(id);

        //根据用户ID检查用户是否存在数据，如果不存在，则抛出异常
        if(standardById==null){
            log.info("结果2:根据id查询详情，结果为null，抛出异常...");
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, "查询用户详情失败，用户数据不存在");
        }

        log.info("结果1:查询出数据,standardById");
        return standardById;
    }

    @Override
    public PageData<AdminListItemVO> list(Integer pageNum) {
        Integer pageSize = 5;
        return list(pageNum, pageSize);
    }

    @Override
    public PageData<AdminListItemVO> list(Integer pageNum, Integer pageSize) {
        log.info("开始:【查询-用户-分页】");
        log.debug("传入参数, pageNum={}, pageSize={}", pageNum, pageSize);
        PageHelper.startPage(pageNum, pageSize);
        List<AdminListItemVO> list = mapper.list();
        PageInfo<AdminListItemVO> pageInfo = new PageInfo<>(list);
        PageData<AdminListItemVO> pageData = PageInfoToPageDataConverter.converter(pageInfo);
        log.debug("结果1: 查询结果：pageData={}", pageData);
        return pageData;
    }
}
